﻿using Cms.Core.Application.IService.PermissionInfo;
using Cms.Core.Infrastructure.AuthHelper;
using Cms.Core.Infrastructure.Common;
using Cms.Core.Infrastructure.Dependency;
using Cms.Core.Infrastructure.Extension;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Reflection;
using Cms.Core.Infrastructure.Log;
using System.Diagnostics;
using Cms.Core.Application.IService.TokenInfo;
using Microsoft.Extensions.Caching.Distributed;
using Cms.Core.Application.Secutiry;
using Cms.Core.Infrastructure.Extension.Caching;

namespace Cms.Core.AspNetMvc.Mvc.Filter
{
    public class AdminAuthorizeFilter : Attribute, IAsyncAuthorizationFilter
    {

        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {


            var authorizeCore = await AuthorizeCoreAsync(context);
            if (!authorizeCore.IsOk)
            {
                HandleUnauthorizedRequest(context, authorizeCore);
            }

        }

        private ITokenService _tokenService = null;

        private IDistributedCache _cache = null;


        protected virtual async Task<AuthorizationResult> AuthorizeCoreAsync(AuthorizationFilterContext context)
        {

            var httpContext = context.HttpContext;
            var requirement = httpContext.RequestServices.GetService<PermissionRequirement>();


            //请求Url
            var questUrl = httpContext.Request.Path.Value.ToLower();
            var authorizationResult = new AuthorizationResult();
            if (questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal))
            {
                return authorizationResult;
            }


            var controllerAction = context.ActionDescriptor as ControllerActionDescriptor;

            var controllerIsContainAllowAnonymous = controllerAction.AsTypeInfo().HasAttribute<AllowAnonymousAttribute>(); //控制器是否包含AllowAnonymous特性

            var actionIsContainallowAnonymous = controllerAction.MethodInfo.HasAttribute<AllowAnonymousAttribute>(); //action是否包含AllowAnonymous特性
            if (controllerIsContainAllowAnonymous|| actionIsContainallowAnonymous) //控制器或者action是否包含AllowAnonymous特性
            {
                return new AuthorizationResult();
            }

            var scheme = httpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
            //判断请求是否拥有凭据
            var defaultAuthenticate = await scheme.GetDefaultAuthenticateSchemeAsync();
            if (defaultAuthenticate.IsNotNull())
            {

                _cache = ServiceLocator.Instance.GetService<IDistributedCache>();

              
                _tokenService = ServiceLocator.Instance.GetService<ITokenService>(); //Token接口
                string authorization = httpContext.Request.Headers["Authorization"]; //得到Bearer
                string token = authorization?.TrimStart("Bearer".ToCharArray()); //得到当前Token
                var tokenDto = await _tokenService.GetToken(token);
                if (tokenDto.Id == Guid.Empty || tokenDto.IsDelete) //无效token
                {
                    return new AuthorizationResult(AuthorizationCode.UnLogin);
                }

                //string key = $"Cms_OnlineUser_{userName}";
                //OnlineUser onlineUser = await _cache.GetAsync<OnlineUser>(key);
                var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
                if ((result?.Principal?.Identity?.IsAuthenticated).AsTo<bool?>().HasValue)
                {
          
                    var scopedDictionary = ServiceLocator.Instance.GetService<ScopedDictionary>();
                    var userName = scopedDictionary.Identity.GetUserName();
               
                    if (!scopedDictionary.IsAdmin) //如果是管理员就可以访问所有,所有权限
                    {
                        var commonPermission = controllerAction.MethodInfo.GetCustomAttributes<CommonPermissionAttribute>().FirstOrDefault();  //共用权限，所有用户，可以访问
                        Guid userId = scopedDictionary.Identity.GetUesrId<Guid>(); //得到用户ID
                        if (commonPermission is null)
                        {

                            var permissionService = httpContext.RequestServices.GetService<IPermissionService>();  //得到权限
                            //判断权限
                            var permissions = (await permissionService.GetUserAuthorizationUrl(userId)).Select(o => new PermissionItem { Role = o.RoleName, Url = o.Url }); //得到当前用户所有角色的权限 //可以放到
                            var isPermission = permissions.GroupBy(g => g.Url).Where(o => o.Key?.ToLower() == questUrl).Any();
                            if (!isPermission)
                            {
                                var loggerHelper = ServiceLocator.Instance.GetService<ILoggerHelper>();
                                loggerHelper.Error(this, $"此{httpContext.Request.Path.Value}路径没有授权");
                                authorizationResult.Type = AuthorizationCode.Unauthorized;

                                return authorizationResult;
                            }
                        }
                        //判断过期时间
                        var expiration = scopedDictionary.Identity.GetExpiration<DateTime>();
                        if (IsTokenExpiration(expiration))
                        {
                            //return await this.RefreshToken(tokenDto, requirement.RefreshCoefficients);
                            return new AuthorizationResult(AuthorizationCode.TokenExpired);


                        }
                    }


                }
                else if (httpContext.Response.Headers?["Token-Expired"].ToString().Equals("true") ?? false)
                {
                    return new AuthorizationResult(AuthorizationCode.TokenExpired);
                    //return await this.RefreshToken(tokenDto, requirement.RefreshCoefficients);
                }

                else
                {
                    authorizationResult.Type = AuthorizationCode.UnLogin;
                    return authorizationResult;


                }
            }

            else
            {
                authorizationResult.Type = AuthorizationCode.UnEvidence;
                return authorizationResult;
                //context.Result = new JsonResult(new { message = AuthorizationCode.UnEvidence.ToDisplayName(), type = (int)AuthorizationCode.UnEvidence });
            }

            return authorizationResult;

        }
        /// <summary>
        /// 重写以实现授权未通过的处理逻辑
        /// </summary>
        /// <param name="context">权限验证上下文</param>
        /// <param name="result">权限检查结果</param>
        protected virtual void HandleUnauthorizedRequest(AuthorizationFilterContext context, AuthorizationResult result)
        {
            AuthorizationCode status = result.Type;
            switch (status)
            {
                case AuthorizationCode.UnEvidence:
                    context.Result = new JsonResult(new { message = AuthorizationCode.UnEvidence.ToDisplayName(), type = status, data = result.Data });
                    break;
                case AuthorizationCode.UnLogin:
                    context.Result = new JsonResult(new { message = AuthorizationCode.UnLogin.ToDisplayName(), type = status, data = result.Data });
                    break;
                case AuthorizationCode.TokenExpired:
                    context.Result = new JsonResult(new { message = AuthorizationCode.TokenExpired.ToDisplayName(), type = status, data = result.Data });
                    break;
                case AuthorizationCode.Unauthorized:
                    context.Result = new JsonResult(new { message = AuthorizationCode.Unauthorized.ToDisplayName(), type = status, data = result.Data });
                    break;
                case AuthorizationCode.RefreshToken:
                    context.Result = new JsonResult(new { message = AuthorizationCode.RefreshToken.ToDisplayName(), type = status, data = result.Data });
                    break;
            }
        }

        /// <summary>
        /// 判断是否过期
        /// </summary>
        /// <returns></returns>
        private bool IsTokenExpiration(DateTime expiration)
        {
            //var expirationValue = claims.FirstOrDefault(o => o.Type == ClaimTypes.Expiration)?.Value;
            //var expiration = expirationValue.AsTo<DateTime>();
            var nowtime = DateTime.Now;

            return expiration >= nowtime ? true : false;
        }

        /// <summary>
        /// 刷新Token
        /// </summary>
        /// <param name="claims"></param>
        /// <param name="userId"></param>
        /// <param name="refreshCoefficients"></param>
        /// <returns></returns>
        //    private async Task<AuthorizationResult> RefreshToken(TokenDto tokenDto, int refreshCoefficients)
        //    {




        //        var expiration = tokenDto.ExpirationTime;
        //        var nowtime = DateTime.Now;
        //        var lastRefreshtime = expiration.AddMinutes(refreshCoefficients);
        //        if (lastRefreshtime >= nowtime)
        //        {
        //            var userService = ServiceLocator.Instance.GetService<IUserService>();
        //            var result = await userService.RefreshToken(tokenDto.CreateUserId.Value);
        //            if (result.Successed)
        //            {
        //                return new AuthorizationResult
        //                {
        //                    Type = AuthorizationCode.RefreshToken,
        //                    Data = result.Data
        //                };
        //            }

        //        }
        //        return new AuthorizationResult(AuthorizationCode.TokenExpired);
        //    }
        //}
    }


    /// <summary>
    /// 权限检查结果
    /// </summary>
    [DebuggerDisplay("{Type}-{Message}")]
    public sealed class AuthorizationResult
    {
        public AuthorizationResult(AuthorizationCode type = AuthorizationCode.Ok) : this(null, type, null)
        {


        }

        public AuthorizationResult(string message, AuthorizationCode type, dynamic data)
        {

            Type = type;
            Message = message;
            Data = data;
        }


        private string _message = null;
        public string Message
        {
            get { return _message ?? Type.ToDisplayName(); }
            set { _message = value; }
        }


        public bool IsOk => Type == AuthorizationCode.Ok;

        public AuthorizationCode Type { get; set; }

        public dynamic Data { get; set; }
    }
}
